package org.lgiavedoni.android.franq.service.db;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.lgiavedoni.android.franq.core.dao.DaoObject;
import org.lgiavedoni.android.franq.core.place.Place;
import org.lgiavedoni.android.franq.core.service.Service;
import org.lgiavedoni.android.franq.service.log.Logger;
import org.lgiavedoni.android.franq.service.phone.ContextService;
import org.lgiavedoni.android.franq.util.date.DateUtil;
import org.lgiavedoni.android.franq.util.file.FileUtil;
import org.lgiavedoni.android.franq.util.resource.ResourceUtil;

import android.content.Context;
import android.os.Environment;

import com.db4o.Db4oEmbedded;
import com.db4o.ObjectContainer;
import com.db4o.config.EmbeddedConfiguration;
import com.db4o.ext.DatabaseClosedException;
import com.db4o.query.Predicate;
import com.db4o.query.Query;

public class DbService extends Service {

	public static final DbService inst = new DbService();
	private static final String IMPLEMENTATIONS_KEY = "daos";
	private static final String DB_FILE = "frank.db4o";
	public ObjectContainer db;

	@Override
	public String getServiceName() {
		return "db";
	}

	@Override
	protected void startInternal() {
	}

	private EmbeddedConfiguration configure() {
		// EmbeddedConfiguration configuration =
		// Db4oEmbedded.newConfiguration();

		EmbeddedConfiguration ec = Db4oEmbedded.newConfiguration();
		ec.common().objectClass(DaoObject.class).objectField("id").indexed(true);
		// For the permanence obj in the place
		ec.common().objectClass(Place.class).cascadeOnUpdate(true);
		ec.common().objectClass(Place.class).cascadeOnDelete(true);
		/*
		 * EmbeddedConfiguration ec = Db4oEmbedded.newConfiguration();
		 * ec.common().objectClass(Position.class).objectField("").
		 */
		// configuration.objectClass(Car.class).objectField("pilot").indexed(true);
		// configuration.lockDatabaseFile(false);

		return ec;
	}

	private String db4oDBFullPath() {
		// Desktop app
		if (ContextService.inst.getApplicationContext() == null) {
			String db = "/db/" + DB_FILE;
			File f = ResourceUtil.inst.getResource(db);
			if (f == null) {
				ResourceUtil.inst.createResource(db);
				f = ResourceUtil.inst.getResource(db);
			}
			String path = f.getAbsolutePath();
			f = null;
			return path;
		}
		// Phone app
		return ContextService.inst.getApplicationContext().getDir("data", Context.MODE_PRIVATE) + "/" + DB_FILE;
	}

	public String exportDataBase() {
		boolean isRunning = isStarted();
		if (isRunning)
			stop();

		try {
			String path = null;
			// Desktop app
			if (ContextService.inst.getApplicationContext() == null) {
				path = db4oDBFullPath() + "." + DateUtil.getDateFileNameFormat();
			} else {
				// Phone app
				path = Environment.getExternalStorageDirectory() + "/frank/" + DB_FILE + "." + DateUtil.getDateFileNameFormat();
			}

			// FileUtil.writeFile(new File(path),
			// FileUtil.readFile(db4oDBFullPath()));
			File newFile = new File(path);
			if (!newFile.exists()) {
				if (!newFile.createNewFile())
					Logger.inst.error("Can't create the file on [" + newFile.getAbsolutePath() + "]");
			}

			FileUtil.copyFile(new File(db4oDBFullPath()), newFile);
			return path;
		} catch (IOException e) {
			Logger.inst.error(e);
		}

		if (isRunning)
			start();

		return null;
	}

	public void deleteDataBase() {
		stop();
		File f = new File(db4oDBFullPath());
		FileUtil.writeFile(f, "");
		f = null;
		start();
	}

	@Override
	protected synchronized void stopInternal() {
		if (db != null) {
			synchronized (db) {
				db.close();
				db.ext();
			}
			db = null;
		}
	}

	public Map<String, Long> countEntyties() {
		Map<String, Long> map = new HashMap<String, Long>();
		Query q = db.query();
		q.constrain(DaoObject.class);
		// q.sortBy(new SortByCreationDate());
		for (Object d : q.execute()) {
			String clazz = d.getClass().getSimpleName();
			if (map.containsKey(clazz)) {
				map.put(clazz, map.get(clazz) + 1);
			} else
				map.put(clazz, 1L);
		}

		return map;
	}

	public Class getEntytiClass(String entity) {
		Map<String, Long> map = new HashMap<String, Long>();
		Query q = db.query();
		q.constrain(DaoObject.class);
		// q.sortBy(new SortByCreationDate());
		for (Object d : q.execute()) {
			String clazz = d.getClass().getSimpleName();
			if (clazz.endsWith(entity)) {
				return d.getClass();
			}
		}

		return null;
	}

	@Override
	public void _run() {
		db = Db4oEmbedded.openFile(configure(), db4oDBFullPath());
	}

	// Available
	private boolean isDbAvailable() {
		return db != null && isStarted();
	}

	public void saveObject(DaoObject object) {
		try {
			if (isDbAvailable()) {
				object.save();
				db.store(object);
				db.commit();
			}
		} catch (DatabaseClosedException dbc) {
			stop();
			Logger.inst.error(dbc);
		} catch (Exception ex) {
			Logger.inst.error(ex);
		}
	}

	public void updateObject(DaoObject object) {
		try {
			if (isDbAvailable()) {
				// object.save();
				db.store(object);
				db.commit();
			}
		} catch (Exception ex) {
			Logger.inst.error(ex);
		}
	}

	public List<DaoObject> query(Predicate p) {
		List<DaoObject> l = new ArrayList();
		try {
			if (isDbAvailable()) {
				l = db.query(p);
				// ((ObjectSetFacade) l).sort(new SortByCreationDate());
			}
		} catch (Exception ex) {
			Logger.inst.error(ex);
		}
		return l;
	}

	public List<DaoObject> getAll(Class clazz) {
		try {
			if (isDbAvailable()) {
				/*
				 * l = db.queryByExample(clazz); ((ObjectSetFacade) l).sort(new
				 * SortByCreationDate());
				 */

				/*
				 * Comparator<Position> c = new Comparator<Position>() { public
				 * int compare(Position a, Position b) { return
				 * a.getCreationDate().compareTo(b.getCreationDate()); } };
				 * 
				 * return dbService.db.query(new Predicate<Position>() {
				 * 
				 * @Override public boolean match(Position arg0) { // TODO
				 * Auto-generated method stub return true; } }, c);
				 */
				Query q = db.query();
				q.constrain(clazz);
				// q.sortBy(new SortByCreationDate());
				List l = q.execute();
				// Collections.sort(l, new SortByCreationDate());
				return l;
			}
		} catch (Exception ex) {
			Logger.inst.error(ex);
		}
		return new ArrayList();
	}

	public void delete(DaoObject obj) {
		if (isDbAvailable()) {
			db.delete(obj);
			db.commit();
		}
	}

	public List<?> queryByExample(Object example) {
		List<DaoObject> l = new ArrayList();
		try {

			if (isDbAvailable()) {
				l = db.queryByExample(example);
				// ((ObjectSetFacade) l).sort(new SortByCreationDate());
			}
		} catch (Exception ex) {
			Logger.inst.error(ex);
		}
		return l;
	}

	public long count(Class objectClass) {
		Query q = db.query();
		q.constrain(objectClass);
		return q.execute().size();
	}

}
